//-
// ==========================================================================
// Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors.  All 
// rights reserved.
//
// The coded instructions, statements, computer programs, and/or related 
// material (collectively the "Data") in these files contain unpublished 
// information proprietary to Autodesk, Inc. ("Autodesk") and/or its 
// licensors, which is protected by U.S. and Canadian federal copyright 
// law and by international treaties.
//
// The Data is provided for use exclusively by You. You have the right 
// to use, modify, and incorporate this Data into other products for 
// purposes authorized by the Autodesk software license agreement, 
// without fee.
//
// The copyright notices in the Software and this entire statement, 
// including the above license grant, this restriction and the 
// following disclaimer, must be included in all copies of the 
// Software, in whole or in part, and all derivative works of 
// the Software, unless such copies or derivative works are solely 
// in the form of machine-executable object code generated by a 
// source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. 
// AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED 
// WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF 
// NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 
// PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR 
// TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS 
// BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL, 
// DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK 
// AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY 
// OR PROBABILITY OF SUCH DAMAGES.
//
// ==========================================================================
//+

#include <string.h>
#include <maya/MIOStream.h>
#include <math.h>

#include <maya/MPxNode.h>
#include <maya/MPxCommand.h>

#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnPlugin.h>

#include <maya/MFnNurbsCurve.h>
#include <maya/MFnNurbsSurface.h>
#include <maya/MFnNurbsCurveData.h>
#include <maya/MFnNurbsSurfaceData.h>

#include <maya/MPointArray.h>
#include <maya/MDoubleArray.h>

#include <maya/MString.h>
#include <maya/MTypeId.h>
#include <maya/MPlug.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>

#include <maya/MGlobal.h>
#include <maya/MItCurveCV.h>
#include <maya/MDagPath.h>

class simpleLoft : public MPxNode
{
public:
						simpleLoft() {};
	virtual				~simpleLoft();

	virtual MStatus		compute( const MPlug& plug, MDataBlock& data );
	MObject				loft( MObject &curve, MObject &surfFn, MStatus &stat );

	static	void*		creator();
	static	MStatus		initialize();

public:
	static	MObject		inputCurve;		// The input curve.
	static	MObject		outputSurface;	// The output curve.
	static	MTypeId		id;				// The IFF type id
};

MTypeId	    simpleLoft::id( 0x80011 );
MObject	    simpleLoft::inputCurve;
MObject	    simpleLoft::outputSurface;

simpleLoft::~simpleLoft() {}

void* simpleLoft::creator()
{
	return new simpleLoft();
}

MStatus simpleLoft::initialize()
{
	MFnTypedAttribute   typedAttr;
	MStatus				stat;

	inputCurve = typedAttr.create( "inputCurve", "in",
			     MFnNurbsCurveData::kNurbsCurve, &stat );

	if ( !stat ) {
		stat.perror("ERROR creating simpleLoft curve attribute");
		return stat;
	}

	outputSurface = typedAttr.create( "outputSurface", "out",
				MFnNurbsSurfaceData::kNurbsSurface, &stat );

	if ( !stat ) {
		stat.perror("ERROR creating simpleLoft surface attribute");
		return stat;
	}
	typedAttr.setStorable( false );

	stat = addAttribute( inputCurve );
		if (!stat) { stat.perror("addAttribute"); return stat;}
	stat = addAttribute( outputSurface );
		if (!stat) { stat.perror("addAttribute"); return stat;}

	stat = attributeAffects( inputCurve, outputSurface );
		if (!stat) { stat.perror("attributeAffects"); return stat;}

	return MS::kSuccess;
}

MObject simpleLoft::loft( MObject &curve, MObject &newSurfData, MStatus &stat )
{
	MFnNurbsSurface surfFn;

	MPointArray cvs;
	MDoubleArray ku, kv;
	MFnNurbsCurve curveFn (curve);

	stat = curveFn.getCVs (cvs, MSpace::kWorld);
	if ( stat != MS::kSuccess )
	{
		cerr << "Error in getting CVs: " << stat << endl;
		return MObject::kNullObj;
	}


	int i, j, k = cvs.length();

	// create knot vectors for U and V

	ku.append( 0.0 );
	ku.append( 0.0 );
	ku.append( 0.0 );

	ku.append( 1.0 );
	ku.append( 1.0 );
	ku.append( 1.0 );

	kv.append( 0.0 );
	kv.append( 0.0 );
	kv.append( 0.0 );

	for ( i = 1; i < k-3; i ++ )
		kv.append( (double) i );

	kv.append( k-3 );
	kv.append( k-3 );
	kv.append( k-3 );

	// create cvs for the surface

	for ( i = 1; i < 4; i++ )
	{
		for ( j = 0; j < k; j++ )
		{
			MPoint point = cvs[j];
			point.z += (double) i;	// loft in Z by 1
			cvs.append( point );
		}
	}


	MObject surf = surfFn.create(
		cvs, ku, kv, 3, 3,
		MFnNurbsSurface::kOpen, MFnNurbsSurface::kOpen,
		false, newSurfData, &stat );

	if ( stat != MS::kSuccess )
	{
		cerr << "Error in creating surface: " << stat << endl;
		return MObject::kNullObj;
	}

	// stat = MGlobal::addToModel( surf );
	return surf;

}

MStatus simpleLoft::compute( const MPlug& plug, MDataBlock& data )
{
	MStatus stat;

	if ( plug == outputSurface )	// loft inputCurve into surface
	{
		MDataHandle inputData = data.inputValue( inputCurve, &stat );
		if( stat != MS::kSuccess )
		{
			cerr << "ERROR getting data: " << stat << endl;
			return stat;
		}

		MObject curve = inputData.asNurbsCurve();
		MFnNurbsCurve curveFn( curve, &stat );
		if( stat != MS::kSuccess )
		{
		  cerr << "ERROR creating curve function set:" << stat << endl;
		  return stat;
		}

		MDataHandle surfHandle = data.outputValue( simpleLoft::outputSurface );
		if( stat != MS::kSuccess )
		{
		  cerr << "Error in getting data handle: " << stat << endl;
		  return stat;
		}
		
		MFnNurbsSurfaceData dataCreator;
		MObject newSurfData = dataCreator.create( &stat );
		if ( stat != MS::kSuccess ) {
		  cerr << "Error creating new nurbs surface data block: "
			   << stat << endl;
		  return stat;
		}
		
		/* MObject newSurf = */ loft( curve, newSurfData,  stat );
		if( stat != MS::kSuccess )
		{
		  cerr << "Error in creating surface: " << stat << endl;
		  return stat;
		}
		
		// newSurf is the new surface object, but it has been packed
		// into the datablock we created for it, and the data block
		// is what we must put onto the plug.
		surfHandle.set( newSurfData );
		
		stat = data.setClean( plug );
		if( stat != MS::kSuccess )
		{
		  cerr << "Error in cleaning outputSurface plug: "
			   << stat << endl;
		  return stat;
		}
	}
	else
	{
		return MS::kUnknownParameter;
	}

	return MS::kSuccess;
}

MStatus initializePlugin( MObject obj )
{
	MStatus   status;
	MFnPlugin plugin( obj, PLUGIN_COMPANY, "3.0", "Any");

	status = plugin.registerNode( "simpleLoft", simpleLoft::id,
								  simpleLoft::creator, simpleLoft::initialize );
	if (!status) {
		status.perror("registerNode");
		return status;
	}

	return status;
}

MStatus uninitializePlugin( MObject obj)
{
	MStatus   status;
	MFnPlugin plugin( obj );

	status = plugin.deregisterNode( simpleLoft::id );
	if (!status) {
		status.perror("deregisterNode");
		return status;
	}

	return status;
}
